/**
 * 
 */
package it.uniroma3.dia.digraph.algorithm.utility.stackedtri.barycentric;

import it.uniroma3.dia.digraph.algorithm.utility.stackedtri.StackedTriNode;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.graphdrawing.graphml.xmlns.NodeType;

/**
 * @author Cionzo
 *
 */
public class CombinationNode {
    
    private StackedTriNode source, target;
    private int depth;
    Map<CombinationNode,double[]> parent2lambda;
    double[] lambdas;
    double t;
    private String id;
    
    public CombinationNode(StackedTriNode source, StackedTriNode target, Map<String, CombinationNode> nodeMap) {
	this.source = source;
	this.id = source.getId();
	this.target = target;
	this.depth = source.getDepth();
	this.parent2lambda = null;
	this.lambdas = null;
	this.t=0;
	this.initParent2lambda(nodeMap);
    }
    
    
    /**
     * @param nodeMap 
     */
    private void initParent2lambda(Map<String, CombinationNode> nodeMap) {
	if (this.depth!=0) {
	    StackedTriNode[] parents = source.getParents();
	    this.parent2lambda = new HashMap<CombinationNode, double[]>();
	    this.lambdas = new double[parents.length];
	    double[] sourceLambdas = source.lambdas;
	    double[] targetLambdas = target.lambdas;
	    String parentId;
	    double sLambda, tLambda;
	    CombinationNode parent;

	    for (int i=0;i<parents.length;i++) {
		parentId = parents[i].getId();
		parent = nodeMap.get(parentId);
		sLambda = sourceLambdas[i];
		tLambda = targetLambdas[i];

		this.parent2lambda.put(parent, new double[] {sLambda, tLambda});

	    } 
	    
	}
	nodeMap.put(this.source.getId(), this);
    }
    
    /**
     * @param t
     * @param rest
     * @return
     */
    public double getX(double t, double rest) {
	if (this.depth == 0) {
	    return this.source.getX()*rest + this.target.getX()*t;
	}
	
	double x = 0;
	
	if (this.t!=t)
	    updateLambdas(t, rest);
	
	int i=0;
	for (CombinationNode parent : this.parent2lambda.keySet()) {
	    x += this.lambdas[i]*parent.getX(t, rest);
	    i++;
	}
	
	return x;
    }
    
    /**
     * @param t
     * @param rest
     * @return
     */
    public double getY(double t, double rest) {
	if (this.depth==0) 
	    return this.source.getY()*rest + this.target.getY()*t;
	double y=0;
	
	if (this.t!=t)
	    this.updateLambdas(t, rest);
	int i=0;
	for (CombinationNode parent : this.parent2lambda.keySet()) {
	    y += this.lambdas[i]*parent.getY(t, rest);
	    i++;
	}
	
	return y;
    }


    /**
     * @param t
     * @param rest
     * @param i
     */
    private void updateLambdas(double t, double rest) {
	double[] stLambdas;
	int i=0;
	for (CombinationNode parent : this.parent2lambda.keySet()) {
	    stLambdas = this.parent2lambda.get(parent);
	    this.lambdas[i] = rest*stLambdas[0] + t*stLambdas[1];
	    i++;
	}
	this.t = t;
    }


    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + ((source == null) ? 0 : source.hashCode());
	return result;
    }


    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
	if (this == obj)
	    return true;
	if (obj == null)
	    return false;
	if (getClass() != obj.getClass())
	    return false;
	CombinationNode other = (CombinationNode) obj;
	if (source == null) {
	    if (other.source != null)
		return false;
	} else if (!source.equals(other.source))
	    return false;
	return true;
    }


    /**
     * @return
     */
    public String getId() {
	return this.id;
    }


    /**
     * @return the parent2lambda
     */
    public Map<CombinationNode, double[]> getParent2lambda() {
        return parent2lambda;
    }


    /**
     * @param parent2lambda the parent2lambda to set
     */
    public void setParent2lambda(Map<CombinationNode, double[]> parent2lambda) {
        this.parent2lambda = parent2lambda;
    }
  

}
